package com.blue.http.message;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.blue.core.dict.HttpMethod;
import com.blue.core.util.BeanUtil;
import com.blue.core.util.JsonUtil;
import com.blue.http.config.ErrorConfig;
import com.blue.validation.ValidationUtil;

import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
import io.netty.util.CharsetUtil;

/**
 * HTTP 请求参数默认实现类
 * 
 * @author zhengj
 * @since 1.0 2017年3月1日
 */
public class DefaultHttpRequest implements HttpRequest
{
	public static final int MAX_LENGTH = 16384; // 16K
	
	private static Logger logger = LoggerFactory.getLogger(DefaultHttpRequest.class);
	private static HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
	
	private String url; // http 路径
	private Map<String, String> queryMap = new HashMap<>(); // URL参数
	private String queryString; // URL参数
	private HttpMethod httpMethod; // http 请求方法
	private String ip; // 请求IP地址
	private long start; // 开始时间戳
	private long contentLength; // 内容长度
	private ErrorConfig errorConfig = ErrorConfig.getInstance();

	private String content; // POST内容
	private Map<String, String> postMap = new HashMap<>(); // POST参数
	private Map<String, UploadFile> fileMap = new HashMap<>(); // 上传文件

	public DefaultHttpRequest(HttpMethod httpMethod, String ip)
	{
		this.httpMethod = httpMethod;
		this.ip = ip;
		this.start = System.currentTimeMillis();
	}

	/**
	 * 解析Uri
	 * 
	 * @param uri
	 */
	public void parseUri(String uri)
	{
		QueryStringDecoder decoder = new QueryStringDecoder(uri, CharsetUtil.UTF_8, true);
		url = decoder.path();
		List<String> queryList = new ArrayList<>();
		if (decoder.parameters() != null && !decoder.parameters().isEmpty())
		{
			for (Map.Entry<String, List<String>> entry : decoder.parameters().entrySet())
			{
				String value = null;
				if (entry.getValue() != null && !entry.getValue().isEmpty())
				{
					if (entry.getValue().size() == 1)
					{
						value = entry.getValue().get(0);
					}
					else
					{
						value = StringUtils.join(entry.getValue(), ",");
					}
				}
				queryMap.put(entry.getKey(), value);
				queryList.add(entry.getKey() + "=" + value);
			}
		}
		queryString = StringUtils.join(queryList, "&");
	}
	
	
	public void parsePost(FullHttpRequest request, Charset charset, long maxUploadSize)
	{
		contentLength = HttpUtil.getContentLength(request, 0L);
		if (maxUploadSize > 0 && maxUploadSize < contentLength)
			throw errorConfig.getException(ErrorConfig.MAX_UPLOAD);
		
		if (contentLength < MAX_LENGTH) // 数据少
		{
			String content = request.content().toString(charset);
			this.content = content;
		}
		
		HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(factory, request, charset);
		decoder.offer(request);
		try
		{
			while (decoder.hasNext())
			{
				InterfaceHttpData data = decoder.next();
				if (data != null)
				{
					String name = data.getName();
					if (data.getHttpDataType() == HttpDataType.FileUpload)
					{
						FileUpload fileUpload = (FileUpload)data;
						UploadFile uploadFile = new DefaultUploadFile(fileUpload);
						fileMap.put(name, uploadFile);
						logger.info("上传文件：成功 - {}", uploadFile);
					}
					else if (data.getHttpDataType() == HttpDataType.Attribute)
					{
						Attribute attribute = (Attribute)data;
						String value = attribute.getString();
						postMap.put(name, value);
					}
				}
			}
		}
		catch (EndOfDataDecoderException e)
		{
			logger.warn("到达流末尾：");
		}
		catch (IOException e) {
			logger.warn("上传文件出错：", e);
		}
		
	}
	

	@Override
	public <T> T getQueryObject(Class<T> clazz)
	{
		return BeanUtil.createBean(clazz, queryMap);
	}

	@Override
	public <T> T getQueryObject(Class<T> clazz, Class<?>... groups)
	{
		T obj = BeanUtil.createBean(clazz, queryMap);
		if (obj != null)
		{
			ValidationUtil.valid(obj, groups);
		}
		return obj;
	}

	@Override
	public String getUrl()
	{
		return url;
	}

	@Override
	public String getQueryString()
	{
		return queryString;
	}

	@Override
	public String getQueryString(String name)
	{
		return queryMap.get(name);
	}

	@Override
	public int getQueryInt(String name, int defaultVal)
	{
		String str = queryMap.get(name);
		if (str == null || str.isEmpty())
			return defaultVal;
		
		str = str.trim();
		if (str.isEmpty())
			return defaultVal;
		
		try
		{
			defaultVal = Integer.parseInt(str);
		}
		catch (NumberFormatException e)
		{
		}
		return defaultVal;
	}

	@Override
	public double getQueryDouble(String name, double defaultVal)
	{
		String str = queryMap.get(name);
		if (str == null || str.isEmpty())
			return defaultVal;
		
		str = str.trim();
		if (str.isEmpty())
			return defaultVal;
		
		try
		{
			defaultVal = Double.parseDouble(str);
		}
		catch (NumberFormatException e)
		{
		}
		return defaultVal;
	}

	@Override
	public Map<String, String> getQueryStringMap()
	{
		return queryMap;
	}

	@Override
	public String getContent()
	{
		return content;
	}
	
	@Override
	public <T> T getContentObject(Class<T> clazz, Class<?>... groups)
	{
		if (content == null || content.isEmpty())
			return null;
		
		T obj = JsonUtil.decode(content, clazz);
		if (obj != null)
		{
			ValidationUtil.valid(obj, groups);
		}
		return obj;
	}
	
	@Override
	public Map<String, String> getPostMap()
	{
		return postMap;
	}
	
	@Override
	public String getPost(String name)
	{
		return postMap.get(name);
	}
	
	public Map<String, UploadFile> getFileMap()
	{
		return fileMap;
	}
	
	public UploadFile getFile(String name)
	{
		return fileMap.get(name);
	}
	
	public UploadFile getFile()
	{
		Iterator<UploadFile> iterator = fileMap.values().iterator();
		if (iterator.hasNext())
			return iterator.next();
		
		return null;
	}

	@Override
	public HttpMethod getHttpMethod()
	{
		return httpMethod;
	}

	@Override
	public String getIp()
	{
		return ip;
	}

	@Override
	public long getStart()
	{
		return start;
	}

	@Override
	public long getContentLength()
	{
		return contentLength;
	}
	
}
